.. _Метки и goto:

Метки и goto
============

.. index::
    goto
    label
    метка


Метки - это имена, которые присваиваются строкам кода для того, чтобы передать управление на эти строки в дальнейшем.
Метка состоит из имени и символа дветочие:

.. code-block:: d

    end: // ← метка (a label)

Приведенная выше метка дает имя строки, на которой она определена.
*Примечание: На самом деле метки могут существовать между любыми двумя утверждениями и
они дают имя точке, в которой они находятся. Однако такое использование не часто встречается:*

.. code-block:: d

    anExpression(); end: anotherExpression();

goto
-----

goto передает управление  на указанную метку:

.. code-block:: d

    void foo(bool condition)
    {
        writeln("first");
        if (condition)
        {
            goto end;
        }
        writeln("second");
    end:
        writeln("third");
    }

Когда условие истинно, управление переходит к метке end, фактически пропуская
строку, в которой печатается "second":

.. code-block:: console

    first
    third

goto работает так же, как и в языках программирования C и C++. Будучи
печально известным тем, что затрудняет понимание замысла и потока кода, goto
не рекомендуется даже в этих языках. Вместо него следует использовать такие выражения, как if, while, for и т. д.
следует использовать вместо них.
Например, предыдущий код может быть написан без goto в более
структурированном виде:

.. code-block:: d

    void foo(bool condition)
    {
        writeln("first");
        if (!condition)
        {
            writeln("second");
        }
        writeln("third");
    }

Однако в C есть два допустимых варианта использования goto, и ни один из них не является необходимым
в D.

**Область финализации**

Одно из допустимых применений goto в языке C - переход в область финализации, где выполняются
операции по очистке функции (например, возврат ресурсов,
отмена некоторых операций и т. д.):

.. code-block:: c

    // --- C code ---
    int foo()
    {
        // ...
        if (error)
        {
            goto finally
                ;
        }
        // ...
        finally
            : // ... cleanup operations ...
            return error;
    }

Такое использование goto не является необходимым в D, поскольку существуют другие способы управления
ресурсами: сборщик мусора, деструкторы, блоки catch и finally,
операторы scope() и т. д.
Примечание: В C++ использование goto также не обязательно.

**continue и break для внешних циклов**

Еще одно допустимое использование goto в Си - это внешние циклы.
Поскольку continue и break влияют только на внутренний цикл, один из способов продолжить или
выхода из внешнего цикла с помощью операторов goto:

.. code-block:: c

    // --- C code ---
    while (condition)
    {
        while (otherCondition)
        {
            // affects the inner loop
            continue;
            // affects the inner loop
            break;
            // works like 'continue' for the outer loop
            goto continueOuter;
            // works like 'break' for the outer loop
            goto breakOuter;
        }
    continueOuter:
        ;
    }
    breakOuter:

Этот же прием можно использовать и для внешних операторов switch.
В D такое использование goto не нужно, потому что в D есть метки циклов, которые мы увидим ниже.
ниже.
*Примечание: Подобное использование goto можно встретить и в C++*

**Проблема пропуска конструкторов**

Конструктор вызывается на объекте именно там, где этот объект определен. Это происходит
главным образом потому, что информация, необходимая для построения объекта, обычно
не доступна до этого момента. Кроме того, нет необходимости конструировать объект, если этот
объект вообще не будет использоваться в программе.
Когда goto пропускает строку, на которой строится объект, программа может
использовать объект, который еще не был подготовлен:

.. code-block:: d

    if (condition)
    {
        goto aLabel; // skips the constructor
    }
    auto s = S(42); // constructs the object properly
    aLabel:
    s.bar(); // BUG: 's' may not be ready for use

Компилятор предотвращает эту ошибку:

.. code-block:: console

Error: goto skips declaration of variable deneme.main.s

Метки циклов
------------

.. index::
    метки циклов
    goto in case

У циклов могут быть метки, а операторы goto могут ссылаться на эти метки

.. code-block:: d

    outerLoop: while (condition)
    {
        while (otherCondition)
        {
            // affects the inner loop
            continue;
            // affects the inner loop
            break;
            // continues the outer loop
            continue outerLoop;
            // breaks the outer loop
            break outerLoop;
        }
    }

Операторы switch также могут иметь метки. Внутренний оператор break может ссылаться на
на внешний оператор switch, чтобы выйти за пределы внешнего оператора switch.

goto в секциях case
-------------------

Мы уже рассматривали использование goto в разделах case в главе :ref:`Оператор switch и case<глава_30_start>`:
* goto case вызывает продолжение выполнения к следующему случаю.
* goto default приводит к продолжению выполнения к разделу по умолчанию.
* goto case expression вызывает продолжение выполнения к случаю, который
соответствует этому выражению

Резюме
------

* Некоторые из вариантов использования goto не нужны в D.
* В метках break и continue можно указывать метки для воздействия на внешние циклы и операторы switch
  операторов.
* goto внутри разделов case может заставить поток программы перейти к другим разделам case и
  разделам по умолчанию.
